/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cs575.coresystem;

import cs575.dbentities.Session;
import cs575.dbentities.Converter;
import cs575.dbentities.GroupEx;
import cs575.dbentities.GroupInvitation;
import cs575.dbentities.GroupInvitationPK;
import cs575.dbentities.GroupMember;
import cs575.dbentities.GroupMemberPK;
import cs575.dbentities.Role;
import cs575.dbentities.TaskUser;
import cs575.dbentities.User;
import cs575.dbentities.Task;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

/**
 *
 * @author bms
 */
public class Facade {

    private EntityManagerFactory entityManagerFactory;
    private EntityManager entityManager;
    private Accounting logger = new Accounting();
    private NewAuthentication auth;
    private Authorization authoriz;
    private GroupManager group;
    private UserManager user;
    private TaskManagerE tm;
    private Session session;

    public Facade() {
        entityManagerFactory = Persistence.createEntityManagerFactory("TasksManagementPU");
        entityManager = entityManagerFactory.createEntityManager();
        auth = new NewAuthentication(entityManager);
        group = new GroupManager(entityManager);
        user = new UserManager(entityManager);
        tm = new TaskManagerE(entityManager);
    }

    // <editor-fold defaultstate="collapsed" desc="basic services-methods">
    public Session login(String username, String password) {
        session = auth.authenticate(username, password);
        authoriz = new Authorization();
        return session;
    }

    public Boolean logout(Session currentSession) {
        return auth.endSession(currentSession);
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="user-methods">
    public boolean createUser(String userName, String email, String password) {        
        User newUser = _createUser(userName, email, password, group.retrieveGroup(1));
        if(newUser != null)
        {
            GroupEx prvGroup = group.createGroup(userName+"_personal"," Personal group for "+userName, newUser);
            
            // TODO: User edit throws a persistence error due to db primary key restrictions
            if(prvGroup != null){
                //user.editUser(new User(newUser.getUserId(), newUser.getUserName(), newUser.getEmail(), newUser.getPassword(), prvGroup));
                return true;
            }
                
        }
//        userRollback(result);
        return false;
    }
    

    public boolean editUser(Session session, int id, String userName, String email, String password) {
        boolean result = false;
        if (validateAuth(session)) {
            if (session.getUserId() == id) // authorization users only allowed to change their own information 
            {
                result = _editUser(id, userName, email, password);
            }
        }
        return result;
    }

    public boolean removeUser(int id) {
        return _removeUser(id);
    }

    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Task-methods">
    /**
     * Create a task in a specified group
     * 
     * @param session
     * @param taskName
     * @param taskDescription
     * @param dueDate
     * @param groupId
     * @return Task
     */
    public Task createTask(Session session, String taskName, String taskDescription, String dueDate, int groupId) {
        if (validateAuth(session)) {
            GroupEx group = new GroupEx(groupId);
            // authorization Admins only allowed to add tasks to a group
            if (authoriz.isAuthorized(group, Authorization.G_add_task_to_group)) {
                return this.tm.createTask(session.getUserId(), taskName, taskDescription, dueDate, group);
            }
        }
        return null;        
    }

    /**
     * Edit a specified task
     * 
     * @param session
     * @param task
     * @return Boolean
     */
    public Boolean editTask(Session session, int taskId, String taskName, String taskDescription, String dueDate, int status) {
        if (validateAuth(session)) {
            Task task = new Task(taskId);
            task.setTaskName(taskName);
            task.setTaskDescription(taskDescription);
            task.setDueDate(dueDate);
            task.setStatus(status);
            // authorization Admins only allowed to edits tasks to a group
            if (authoriz.isAuthorized(task, Authorization.T_edit)) {
                return this.tm.editTask(task);
            }
        }
        return false;         
    }

    /**
     * Remove a specified task
     * 
     * @param session
     * @param taskId
     * @return Boolean
     */
    public Boolean removeTask(Session session, int taskId) {
        if (validateAuth(session)) {
            Task task = new Task(taskId);
            // authorization Admins only allowed to remove tasks to a group
            if (authoriz.isAuthorized(task, Authorization.T_delete)) {
                return this.tm.deleteTask(task);
            }
        }
        return false;  
    }
    
    
    /**
     * Assign a task to a specified user
     * 
     * @param session
     * @param taskId
     * @param destUserId
     * @return Boolean
     */
    public Boolean assignTask(Session session, int taskId, int destUserId){
        if (validateAuth(session)) {
            Task task = tm.retrieveTask(taskId);
            User soucUser = new User(session.getUserId());
            User destUser = new User(destUserId);
            // authorize user for task assignments
            // TODO: Fix permission for task assignment
            if (authoriz.isAuthorized(new GroupEx(task.getGroupId().getGroupId()), Authorization.G_add_task_to_group)) {
                return this.tm.assignTask(task, soucUser, destUser);
            }
        }
        return false;          
    }
    
    
    /**
     * Complete a specified task
     * 
     * @param session
     * @param taskId
     * @return Boolean
     */
    public Boolean completeTask(Session session, int taskId){
        if (validateAuth(session)) {
            Task task = tm.retrieveTask(taskId);
            // only assigned user can complete a given task
            if (authoriz.isAuthorized(task, Authorization.T_complete)) {
                return this.tm.completeTask(taskId);
            }
        }
        return false;  
    }   
    
    
    /**
     * Accept a task assignment
     * 
     * @param session
     * @param taskId
     * @return Boolean
     */
    public Boolean acceptTask(Session session, int taskId){
        if (validateAuth(session)) {
            Task task = tm.retrieveTask(taskId);
            User user = new User(session.getUserId());
            // only assigned user can complete a given task
            if (authoriz.isAuthorized(new GroupEx(task.getGroupId().getGroupId()), Authorization.T_read)) {
                return this.tm.acceptTask(taskId, session.getUserId());
            }
        }
        return false; 
    }
    
    
    /**
     * Reject a task assignment
     * 
     * @param session
     * @param taskId
     * @return Boolean
     */
    public Boolean rejectTask(Session session, int taskId){
        if (validateAuth(session)) {
            Task task = tm.retrieveTask(taskId);
            User user = new User(session.getUserId());            
            // only assigned user can complete a given task
            if (authoriz.isAuthorized(new GroupEx(task.getGroupId().getGroupId()), Authorization.T_read)) {
                return this.tm.rejectTask(taskId, session.getUserId());
            }
        }
        return false; 
    }   
    
    
    /**
     * Finds and returns a task based on a taskId
     * 
     * @param session
     * @param taskId
     * @return Task
     */
    public Task getTask(Session session, int taskId){
        if (validateAuth(session)) {
            Task task = new Task(taskId);
            // only a user with read privileges can get a task
            if (authoriz.isAuthorized(task, Authorization.T_read)) {
                return this.tm.retrieveTask(taskId);
            }
        }
        return null; 
    }    
    

    // </editor-fold>    
    
    // <editor-fold defaultstate="collapsed" desc="group-methods">
    public boolean createGroup(Session session, String groupName, String groupDescription) {
        boolean result = false;
        if (validateAuth(session)) {{
                result = _createGroup(groupName, groupDescription);
            }
        }
        return result;
    }

    public boolean editGroup(Session session, int id, String groupName, String groupDescription) {
        boolean result = false;
        if (validateAuth(session)) {
            // authorization Admins only allowed to edit their groups
            if (authoriz.isAuthorized(new GroupEx(id), Authorization.G_edit_group)) {
                result = _editGroup(id, groupName, groupDescription);
            }
        }
        return result;

    }

    public boolean inviteUserToGroup(Session session, int groupId, int destUser,int roleId, String comment) {
        boolean result = false;
        if (validateAuth(session)) {
            // authorization Admins only allowed to invite users to a group
            if (authoriz.isAuthorized(new GroupEx(groupId), Authorization.G_invite_user)) {
                result = _inviteUserToGroup(groupId, destUser,roleId, comment);
            }
        }
        return result;
    }

        public Boolean setUserRole(Session session, int groupId, int destUser, int roleId) {
                boolean result = false;
        if (validateAuth(session)) {
            // authorization Admins only allowed to invite users to a group
            if (authoriz.isAuthorized(new GroupEx(groupId), Authorization.G_set_member_role)) {
                result = _setUserRole(groupId, destUser, roleId);
            }
        }
        return result;
            
    }
    
    public GroupEx getGroupInfo(Session session, int groupId) {
        if (validateAuth(session)) {
            // TODO: authorization Admins only allowed to invite users to a group
            if (authoriz.isAuthorized(new GroupEx(groupId), Authorization.G_read_Group)) {
                return _getGroupInfo(groupId);
            }
        }
        return null;
    }

    public List<GroupMember> getGroups(Session session, int sourceUser) {
        if (validateAuth(session)) {
            // TODO: authorization Admins only allowed to invite users to a group
            if (session.getUserId() == sourceUser) {
                return _getGroups(sourceUser);
            }
        }
        return null;
    }

        public List<Task> getGroupTasks(Session session, int groupId) {
        if (validateAuth(session)) {
            // TODO: authorization Admins only allowed to invite users to a group
            if (authoriz.isAuthorized(new GroupEx(groupId), Authorization.G_read_Group)) {
                return _getGroupTasks(groupId);
            }
        }
        return null;
    }
    
    
    public List<GroupMember> getGroupMembers(Session session, int groupId) {
        if (validateAuth(session)) {
            // TODO: authorization Admins only allowed to invite users to a group
            if (authoriz.isAuthorized(new GroupEx(groupId), Authorization.G_read_Group)) {
                return _getGroupMembers(groupId);
            }
        }
        return null;
    }

    public Boolean acceptGroupInvitation(Session session, int groupId, int userId) {
        if (validateAuth(session)) {
        if (session.getUserId() == userId){
                return _acceptGroupInvitation(groupId, userId);
            }
        }
        return false;
    }

    public Boolean rejectGroupInvitation(Session session, int groupId, int userId) {
        if (validateAuth(session)) {
            if (session.getUserId() == userId){
                return _rejectGroupInvitation(groupId, userId);
            }
        }
        return false;
    }

    public List<GroupInvitation> getGroupsInvitation(Session session, int userId) {
        if (validateAuth(session)) {
            if (session.getUserId() == userId) 
            {
                return _getGroupsInvitation(userId);
            }
        }
        return null;
    }

    // </editor-fold>
    private boolean validateAuth(Session session) {
        this.session = auth.authenticate(session);
        if(session != null)
        {
            authoriz = new Authorization(session.getUser());
            authoriz.loadData();
            return true;
        }
        return false;
    }

    
    private User _createUser(String userName, String email, String password, GroupEx prvGroup) {
        User paramUser = user.createUser(userName, email, password, prvGroup);
        return paramUser;
    }

    private boolean _editUser(int id, String userName, String email, String password) {
        return user.editUser(new User(id, userName, password, email));
    }

    private boolean _removeUser(int id) {
        // this method is not supported right now 
        return false;
    }

    private boolean _createGroup(String groupName, String groupDescription) {
        GroupEx param = group.createGroup(groupName, groupDescription, user.createUserReference(session.getUserId()));
        if (param != null) {
            return true;
        } else {
            return false;
        }
    }

    private boolean _editGroup(int groupId, String groupName, String groupDescription) {
        return group.editGroup(new GroupEx(groupId, groupName, groupDescription));
    }

    private boolean _inviteUserToGroup(int groupId, int destUser, int roleId,String comment) {
        return group.inviteUser(new GroupEx(groupId), new User(session.getUserId()), new User(destUser), new Role(roleId), comment);

    }

    Boolean _acceptGroupInvitation(int groupId, int retrieveUser) {
        return group.acceptInvitation(group.createGroupReference(groupId), user.createUserReference(retrieveUser));
    }

    Boolean _rejectGroupInvitation(int GroupEx, int retrieveUser) {
        return group.rejectInvitation(group.createGroupReference(GroupEx), user.createUserReference(retrieveUser));
    }

    List<GroupInvitation> _getGroupsInvitation(int retrieveUser) {
        User res = user.retrieveUser(retrieveUser);
        return res.getGroupInvitations();
    }

    private GroupEx _getGroupInfo(int groupId) {
        return group.retrieveGroup(groupId);
    }

    private List<GroupMember> _getGroups(int sourceUser) {
        return group.getGroups(new User(sourceUser));
    }

    private List<GroupMember> _getGroupMembers(int groupId) {
        return group.getGroupMember(new GroupEx(groupId));

    }

    private boolean _setUserRole(int groupId, int destUser, int roleId) {
        return group.setUserRole(new GroupEx(groupId),new User(destUser),new Role(roleId));
    }

    private void userRollback(User user) {
        removeUser(user.getUserId());
    }

    private List<Task> _getGroupTasks(int groupId) {
        return group.getGroupTasks(new GroupEx(groupId));
    }

    public List<Task> _getTasks(Session session) {
        return tm.getTasks(session.getUserId());
    }

    public List<Task> getTasks(Session session) {
         if (validateAuth(session)) { 
            {
                return _getTasks(this.session);
            }
        }
        return null;
    }

    public Task createTask(Session session, String taskName, String taskDescription, String dueDate) {
       if (validateAuth(session)) {
            GroupEx pvtGroup =  group.retrievePrivateGroup(session.getUserId());
                return this.tm.createTask(session.getUserId(), taskName, taskDescription, dueDate, pvtGroup);
       }
       return null;
    }

    public List<Task> getPendingTasks(Session session) {
           if (validateAuth(session)) { 
            {
                return _getPendingTasks(this.session);
            }
        }
        return null;
    }

    private List<Task> _getPendingTasks(Session session) {
        return tm.getPendingTasks(session.getUserId());
    }

    public List<Task> getCompletedTasks(Session session) {
        return tm.getCompletedTasks(session.getUserId());
    }
}
